iT邦幫忙

2023 iThome 鐵人賽

DAY 24
0
Security

從自建漏洞中學習 - 一起填坑吧系列 第 24

Auth 應用程式 - Authentication 認證篇 2 - 加入 Error 處理

  • 分享至 

  • xImage
  •  

Auth 應用程式 - Authentication 認證篇 2 - 加入 Error 處理

JWT 回顧

在介紹完 Error 之後,還記得我們的 JWT 嗎? 幫大家回顧一下 ~

我們在 Auth Controller 中做了一個 protect function:

protect function:

exports.protect = async (req, res, next) => {
    
    // 1. 取得 token 
    let token;

    // 此處會依照開頭是否是 Bearer 來作為判斷依據的原因:
    // 因為這邊預設 request 的 header 是帶上: "Bearer ${JWT_TOKEN}" 
    if(
      req.headers.authentication &&
      req.headers.autrhntication.startWith('Bearer')
    ) {
        token = req.headers.authorization.split(' ')[1];
    }
    
    if (!token) {
        return next (
            new AppError('Please Log in to get access', 401);
        );
    }
    
    // 2. 驗證 token
    // 此處使用 promisify 是想要把 jwt.verify 轉換成 promise 版本,避免 Error first call-back
    const decoded = await promisify(jwt.verify)(token, process.env.JWT_SECRET);
    
    
    // 3. 確認 user 是否還存在
    const freshUser = await User.findById(decoded.id);
    if(!freshUser) {
        return next(new AppError('The token belonging to this user does no longer exist.', 401));
    }
    
    // 4. 確認是否在 token 發出去後,user 改動了 password
    // 此處使用到的 changedPasswordAfter function 寫在下一個程式碼區塊
    // 這邊的 iat 是指 指示 JWT 發佈時間的時間戳,有興趣可以回之前的規劃篇看看 ~
    if (freshUser.changedPasswordAfter(decoded.iat)) {
        return next(new AppError('Password has been changed recently. Please login again.', 401));
    }
    
    // 5. 允許進入被受保護的 route
    req.user = freshUser;
    
    next();
    
}

我們在這邊實作了:

  1. 取得 token -> 處理相關 Error
  2. 驗證 token
  3. 確認 user 是還存在
  4. 確認是否在 token 發出去後,user 改動了 password
  5. 最後,指派驗證完後的 freshUser 給 req.user

加入 JWT Error 處理

在 Access 時,帶入的 signature 是錯誤的話,會噴出 name 為 'JsonWebTokenError' 的錯誤。
除此之外,我們還會遇到 token 過期的情形,這時我們就需要處理 'TokenExpiredError' 的錯誤。

而針對這些錯誤我們可以將它統一在 error Controller 中處理:

error controller:

  module.exports = (err, req, res, next) => {
      
      err.statusCode = err.statusCode || 500;
      err.status = err.status || 'error';
      
      if (process.env.NODE_ENV === 'development') {
          sendDevelopmentError(err, res);
      } else if (process.env.NODE_ENV === 'prodcution') {
          let error = { ...err };

          // 處理 DB 相關的 error
          if (error.name === 'CastError') error = handleCastErrorInDB(error);
          // 此處省略處理其他 DB 相關的 error 
          
          // 此處處理 JWT 的 error
          // JsonWebTokenError
          if (error.name === 'JsonWebTokenError') error = handleJWTError(error);
          // TokenExpiredError
          if (error.name === 'TokenExpiredError') error = handleJWTExpiredError(error);
          
          sendProductionError(err, res);
      }
      
  }

handleJWTError function:

const handleJWTError = err => new AppError('Invalid token, please log in again!', 401);

handleJWTExpiredError function:

const handleJWTExpiredError = err => new AppError('Your token has expired, please log in again!', 401);

今日小心得

今天的介紹就大概就到這邊~ 整合了先前介紹的 JWT & Error,接下來就會進到 reset password 啦~


Reference


上一篇
Auth 應用程式 - Error 處理篇之 3
下一篇
Auth 應用程式 - Reset passord 篇之 1
系列文
從自建漏洞中學習 - 一起填坑吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言